package jwtauth

import (
	"github.com/dgrijalva/jwt-go"
	"kubeInstall/conf"
	"kubeInstall/pkg/auth"
	"kubeInstall/pkg/auth/jwtauth/store/buntdb"
	"kubeInstall/pkg/logger"
	"time"
)

type jwtAuth struct {
	store Storer
}

var Auth *jwtAuth

func init() {
	store := buntdb.NewStore(":memory:")
	Auth = &jwtAuth{
		store: store,
	}
	logger.Info("init jwt auth successfully")
}

func (a *jwtAuth) GenerateToken(userId string, isAdmin bool) (*auth.TokenInfo, error) {
	now := time.Now()
	expiresAt := now.Add(time.Duration(7200) * time.Second).Unix()

	claims := auth.Claims{
		IsAdmin: isAdmin,
		StandardClaims: jwt.StandardClaims{
			ExpiresAt: expiresAt,
			Issuer:    "kube-install",
			Subject:   userId,
		},
	}

	tokenClaims := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	token, err := tokenClaims.SignedString([]byte(conf.AppSetting.JwtSecret))

	return &auth.TokenInfo{
		AccessToken: token,
		ExpiresAt:   expiresAt,
	}, err

}

func (a *jwtAuth) ParseToken(token string) (*auth.Claims, error) {
	err := a.callStore(func(store Storer) error {
		if exists, err := store.Check(token); err != nil {
			return err
		} else if exists {
			return auth.ErrInvalidToken
		}
		return nil
	})

	if err != nil {
		return nil, err
	}

	tokenClaims, err := jwt.ParseWithClaims(token, &auth.Claims{}, func(token *jwt.Token) (interface{}, error) {
		return []byte(conf.AppSetting.JwtSecret), nil
	})

	if tokenClaims != nil {
		if claims, ok := tokenClaims.Claims.(*auth.Claims); ok && tokenClaims.Valid {
			return claims, nil
		}
	}

	return nil, err
}

func (a *jwtAuth) DestroyToken(tokenString string) (string, error) {

	claims, err := a.ParseToken(tokenString)
	if err != nil {
		return "", err
	}

	return claims.Subject, a.callStore(func(store Storer) error {
		expired := time.Unix(claims.ExpiresAt, 0).Sub(time.Now())
		return store.Set(tokenString, expired)
	})

}

func (a *jwtAuth) ParseUserID(tokenString string) (string, error) {
	if tokenString == "" {
		return "", auth.ErrInvalidToken
	}

	claims, err := a.ParseToken(tokenString)
	if err != nil {
		return "", err
	}

	return claims.Subject, nil
}

// Release 释放资源
func (a *jwtAuth) Release() error {
	return a.callStore(func(store Storer) error {
		return store.Close()
	})
}

func (a *jwtAuth) callStore(fn func(Storer) error) error {
	if store := a.store; store != nil {
		return fn(store)
	}
	return nil
}
